package jkcp;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;

import java.nio.ByteOrder;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;

public class IKCPSEG {
	// =====================================================================
	//
	// KCP - A Better ARQ Protocol Implementation
	// skywind3000 (at) gmail.com, 2010-2011
	//
	// Features:
	// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
	// + Maximum RTT reduce three times vs tcp.
	// + Lightweight, distributed as a single source file.
	//
	// =====================================================================

	// =====================================================================
	// KCP BASIC
	// =====================================================================
	private static final int IKCP_RTO_NDL = 30; // no delay min rto
	private static final int IKCP_RTO_MIN = 100; // normal min rto
	private static final int IKCP_RTO_DEF = 200;
	private static final int IKCP_RTO_MAX = 60000;
	/**推送消息**/
	private static final int IKCP_CMD_PUSH = 81; // 
	/**ack消息**/
	private static final int IKCP_CMD_ACK = 82; // cmd: ack
	/****/
	private static final int IKCP_CMD_WASK = 83; // cmd: window probe (ask)
	private static final int IKCP_CMD_WINS = 84; // cmd: window size (tell)
	private static final int IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK
	private static final int IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS
	private static final int IKCP_WND_SND = 32;
	private static final int IKCP_WND_RCV = 32;
	private static final int IKCP_MTU_DEF = 1400;
	private static final int IKCP_ACK_FAST = 3;
	private static final int IKCP_INTERVAL = 100;
//	/**头长度**/
	private static final int IKCP_OVERHEAD = 24;
	private static final int IKCP_DEADLINK = 10;
	private static final int IKCP_THRESH_INIT = 2;
	private static final int IKCP_THRESH_MIN = 2;
	private static final int IKCP_PROBE_INIT = 7000; // 7 secs to probe window
														// size
	private static final int IKCP_PROBE_LIMIT = 120000; // up to 120 secs to
														// probe window

	// ---------------------------------------------------------------------
	// encode / decode
	// ---------------------------------------------------------------------

	/* encode 8 bits unsigned int */
	public static void ikcp_encode8u(ByteBuf p, int c) {
		p.writeByte(c);
	}

	/* decode 8 bits unsigned int */
	public static short ikcp_decode8u(ByteBuf p) {
		return p.readUnsignedByte();
	}

	/* encode 16 bits unsigned int (lsb) */
	public static void ikcp_encode16u(ByteBuf p, int w) {
		p.writeShort(w);
	}

	/* decode 16 bits unsigned int (lsb) */
	public static int ikcp_decode16u(ByteBuf p) {
		return p.readUnsignedShort();
	}

	/* encode 32 bits unsigned int (lsb) */
	public static void ikcp_encode32u(ByteBuf p, long w) {
		p.writeInt((int) w);
	}

	/* decode 32 bits unsigned int (lsb) */
	public static long ikcp_decode32u(ByteBuf p) {
		return p.readUnsignedInt();
	}

	public static long _imin_(long a, long b) {
		return Math.min(a, b);
	}

	public static long _imax_(long a, long b) {
		return Math.max(a, b);
	}

	public static long _ibound_(long lower, long middle, long upper) {
		return _imin_(_imax_(lower, middle), upper);

	}

	public static int _itimediff(long later, long earlier) {
		return ((int) (later - earlier));

	}

	// ---------------------------------------------------------------------
	// manage segment
	// ---------------------------------------------------------------------
	long conv;
	long cmd;
	/**一个大包分成多个小包，此时每个小包的编号   从大到小排列  3 2 1**/
	long frg;
	long wnd;
	long ts;
	long sn;
	long una;
	/**包的长度**/
	long _len;
	long resendts;
	long rto;
	long fastack;
	long xmit;
	ByteBuf data; // 1 size

	/*
	 * static void* (*ikcp_malloc_hook)(size_t) = nil static void
	 * (*ikcp_free_hook)(void *) = nil
	 * 
	 * // internal malloc static void* ikcp_malloc(size_t size) { if
	 * (ikcp_malloc_hook) return ikcp_malloc_hook(size) return malloc(size) }
	 * 
	 * // internal free static void ikcp_free(void *ptr) { if (ikcp_free_hook) {
	 * ikcp_free_hook(ptr) } else { free(ptr) } } // redefine allocator void
	 * ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)) {
	 * ikcp_malloc_hook = new_malloc ikcp_free_hook = new_free } // allocate a
	 * new kcp segment
	 */

	public static IKCPSEG ikcp_segment_new(int size) {
		IKCPSEG newInfo = new IKCPSEG();
		newInfo.data = PooledByteBufAllocator.DEFAULT.directBuffer(size);
		newInfo.data.order(ByteOrder.LITTLE_ENDIAN);
		return newInfo;
	}

	// delete a segment

	// write log
	// func Ikcp_log(kcp *Ikcpcb, mask int32 , head string, args...interface{})
	// {
	// //if ((mask & kcp.logmask) == 0 || kcp.writelog == 0) { return }
	// //fmt.Printf(head, args...)
	// }

	// check log mask
	// func ikcp_canlog(kcp *Ikcpcb , mask int32) int32 {
	// if ((mask & kcp.logmask) == 0 || kcp.writelog == nil) { return 0 }
	// return 1
	// }

	// output segment
	public static int ikcp_output(Ikcpcb kcp, ByteBuf data, int size) {

		// if ikcp_canlog(kcp, IKCP_LOG_OUTPUT)!= 0 {
		// Ikcp_log(kcp, IKCP_LOG_OUTPUT, "[RO] %ld bytes", int32(size))
		// }
		if (size == 0) {
			return 0;
		}

		return kcp.iOutPut.Output(data, size, kcp, kcp.user);
	}

	public static Ikcpcb Ikcp_create(long conv, Object user) {
		Ikcpcb kcp = new Ikcpcb();
		kcp.conv = conv;
		kcp.user = user;
		kcp.snd_una = 0;
		kcp.snd_nxt = 0;
		kcp.rcv_nxt = 0;
		kcp.ts_recent = 0;
		kcp.ts_lastack = 0;
		kcp.ts_probe = 0;
		kcp.probe_wait = 0;
		kcp.snd_wnd = IKCP_WND_SND;
		kcp.rcv_wnd = IKCP_WND_RCV;
		kcp.rmt_wnd = IKCP_WND_RCV;
		kcp.cwnd = 0;
		kcp.incr = 0;
		kcp.probe = 0;
		kcp.mtu = IKCP_MTU_DEF;
		kcp.mss = kcp.mtu - IKCP_OVERHEAD;

		kcp.buffer = PooledByteBufAllocator.DEFAULT
				.buffer((int) ((kcp.mtu + IKCP_OVERHEAD) * 3));

		kcp.snd_queue = new LinkedList<IKCPSEG>();
		kcp.rcv_queue = new LinkedList<IKCPSEG>();
		kcp.snd_buf = new LinkedList<IKCPSEG>();
		kcp.rcv_buf = new LinkedList<IKCPSEG>();
		kcp.nrcv_buf = 0;
		kcp.nsnd_buf = 0;
		kcp.nrcv_que = 0;
		kcp.nsnd_que = 0;
		kcp.state = 0;
		kcp.acklist = null;
		kcp.ackblock = 0;
		kcp.ackcount = 0;
		kcp.rx_srtt = 0;
		kcp.rx_rttval = 0;
		kcp.rx_rto = IKCP_RTO_DEF;
		kcp.rx_minrto = IKCP_RTO_MIN;
		kcp.current = 0;
		kcp.interval = IKCP_INTERVAL;
		kcp.ts_flush = IKCP_INTERVAL;
		kcp.nodelay = 0;
		kcp.updated = 0;
		kcp.logmask = 0;
		kcp.ssthresh = IKCP_THRESH_INIT;
		kcp.fastresend = 0;
		kcp.nocwnd = 0;
		kcp.xmit = 0;
		kcp.dead_link = IKCP_DEADLINK;
		kcp.iOutPut = null;
		// kcp.writelog = null;
		return kcp;
	}

	// ---------------------------------------------------------------------
	// release a new kcpcb
	// ---------------------------------------------------------------------
	public static void Ikcp_release(Ikcpcb kcp) {
		if (kcp == null)
			return;
		kcp.nrcv_buf = 0;
		kcp.nsnd_buf = 0;
		kcp.nrcv_que = 0;
		kcp.nsnd_que = 0;
		kcp.ackcount = 0;
		kcp.acklist = null;
		kcp.buffer.release();
		kcp.buffer = null;
	}

	/**
	 * 收到数据
	 * 
	 * @param kcp
	 * @param buffer
	 *            收到的bytebuff
	 * @param _len
	 *            整个包长度
	 * @return
	 */
	public static int Ikcp_recv(Ikcpcb kcp, ByteBuf buffer, int _len) {

		int ispeek = 1;
		if (_len >= 0) {
			ispeek = 0;
		}

		int _recover = 0;

		if (kcp.rcv_queue.size() == 0) {
			return -1;
		}

		_len = Math.abs(_len);

		int peeksize = Ikcp_peeksize(kcp);

		if (peeksize < 0) {
			return -2;
		}

		if (peeksize > _len) {
			return -3;
		}

		if (kcp.nrcv_que >= kcp.rcv_wnd) {
			_recover = 1;
		}

		// if kcp.user[0] == 0 {
		// fmt.Println("have!!!!")
		// }
		// merge fragment
		_len = 0;
		Iterator<IKCPSEG> it = kcp.rcv_queue.iterator();
		while (it.hasNext()) {
			IKCPSEG seg = it.next();
			if (buffer.readableBytes() > 0) {
				buffer.readBytes(seg.data, (int) seg._len);
			}
			_len += (int) seg._len;
			int fragment = (int) seg.frg;
			if (ispeek == 0) {
				it.remove();
				kcp.nrcv_que--;
			}
			if (fragment == 0) {
				break;
			}
		}

		// 把收到的无序的包按顺序排起来
		it = kcp.rcv_buf.iterator();
		while (it.hasNext()) {
			IKCPSEG seg = it.next();
			if (seg.sn == kcp.rcv_nxt && kcp.nrcv_que < kcp.rcv_wnd) {
				it.remove();
				kcp.nrcv_buf--;
				kcp.rcv_queue.addLast(seg);
				kcp.nrcv_que++;
				// if kcp.user[0] == 0 {
				// fmt.Println("insert from recvqueue", kcp.rcv_queue.Len(),
				// kcp.user, "rcv q:", kcp.nrcv_que)
				// }
				kcp.rcv_nxt++;
			} else
				break;
		}

		// fast _recover
		if (kcp.nrcv_que < kcp.rcv_wnd && _recover != 0) {
			// ready to send back IKCP_CMD_WINS in Ikcp_flush
			// tell remote my window size
			kcp.probe |= IKCP_ASK_TELL;
		}
		return _len;
	}

	// ---------------------------------------------------------------------
	// send data
	// ---------------------------------------------------------------------
	
	/**
	 * 获得最近一个包的长度
	 * @param kcp
	 * @return
	 */
	public static int Ikcp_peeksize(Ikcpcb kcp) {
		int length = 0;

		if (kcp.rcv_queue.size() == 0) {
			return -1;
		}
		IKCPSEG seg = kcp.rcv_queue.getFirst();

		if (seg.frg == 0) {
			return (int) seg._len;
		}
		if (kcp.nrcv_que < seg.frg + 1) {
			return -1;
		}
		for (IKCPSEG p : kcp.rcv_queue) {
			length += (int) p._len;
			if (seg.frg == 0) {
				break;
			}
		}

		return (int) length;
	}
	
	/**
	 * kcp发送封包
	 * 功能：将一个大包打包成多个小包发送
	 * @param kcp
	 * @param buffer
	 * @param _len 发送的包大小
	 * @return 0 包解析成多个小包成功    -1长度(_lenth)<0  -2解析成小包数量>255
	 */
	public static int Ikcp_send(Ikcpcb kcp,byte[] buffer,int _len)
	{
		int count, i = 0;
		if (_len < 0) {
			return -1;
		}

		if (_len <= (int) kcp.mss) {
			count = 1;
		} else {
			count = (int) ((_len + kcp.mss - 1) / kcp.mss);
		}
		if (count > 255) {
			return -2;
		}

		if (count == 0) {
			count = 1;
		}

		for (i = 0; i < count; i++) {
			int size = (int) kcp.mss;
			if (_len <= kcp.mss) {
				size =  _len;
			}
			IKCPSEG seg = ikcp_segment_new(size);
			if (buffer != null && _len > 0) {
				seg.data.writeBytes(buffer);
			}

			seg._len = size;
			seg.frg = count - i - 1;
			kcp.snd_queue.add(seg);
			// if kcp.user[0] == 0 {
			// fmt.Println(kcp.user, "send", kcp.snd_queue.Len())
			// }
			kcp.nsnd_que++;
			_len -= size;
		}
		return 0;
	}
	
	// //---------------------------------------------------------------------
	// // parse ack
	// //---------------------------------------------------------------------

	public static void Ikcp_update_ack(Ikcpcb kcp,int rtt)
	{
		int rto = 0;
		if (kcp.rx_srtt == 0) {
			kcp.rx_srtt = rtt;
			kcp.rx_rttval = rtt / 2;
		} else {
			long delta = rtt - kcp.rx_srtt;
			if (delta < 0) {
				delta = -delta;
			}

			kcp.rx_rttval = (3 * kcp.rx_rttval + delta) / 4;
			kcp.rx_srtt = (7 * kcp.rx_srtt + rtt) / 8;
			if (kcp.rx_srtt < 1) {
				kcp.rx_srtt = 1;
			}
		}
		rto = (int) (kcp.rx_srtt + _imax_(1, 4 * kcp.rx_rttval));
		kcp.rx_rto = _ibound_(kcp.rx_minrto, rto, IKCP_RTO_MAX);
	}
	
	
	public static void ikcp_shrink_buf(Ikcpcb kcp)
	{
		 if (kcp.snd_buf.size() > 0) {
			 IKCPSEG seg = kcp.snd_buf.getFirst();
		 kcp.snd_una = seg.sn;
		 } else {
		 kcp.snd_una = kcp.snd_nxt;
		 //if kcp.user[0] == 0 {
		 //println("set2 snd_una:", kcp.snd_nxt)
		 //}
		 }
	}
	
	
	public static void ikcp_parse_ack(Ikcpcb kcp,long sn)
	{
		if (_itimediff(sn, kcp.snd_una) < 0 || _itimediff(sn, kcp.snd_nxt) >= 0) {
			// fmt.Printf("wi %d,%d  %d,%d\n", sn, kcp.snd_una, sn, kcp.snd_nxt)
			return;
		}
		Iterator<IKCPSEG> it = kcp.snd_buf.iterator();
		while (it.hasNext()) {
			IKCPSEG seg = it.next();
			if (sn == seg.sn) {
				// //println("!!!!!!!")
				it.remove();
				kcp.nsnd_buf--;
				break;
			} else {
				seg.fastack++;
			}
		}
	}

	
	public static void ikcp_parse_una(Ikcpcb kcp,long una)
	{
		Iterator<IKCPSEG> it = kcp.snd_buf.iterator();
		while (it.hasNext()) {
			IKCPSEG seg = it.next();
			if (_itimediff(una, seg.sn) > 0) {
				it.remove();
				kcp.nsnd_buf--;
			} else {
				break;
			}
		}
	}
	
	// //---------------------------------------------------------------------
	// // ack append
	// //---------------------------------------------------------------------
	public static void ikcp_ack_push(Ikcpcb kcp,long ts,long sn)
 {
		long newsize = kcp.ackcount + 1;

		if (newsize > kcp.ackblock) {
			long[] acklist;
			int newblock;
			for (newblock = 8; newblock < newsize; newblock <<= 1) {
			}
			acklist = new long[newblock * 2];
			if (kcp.acklist != null) {
				for (int x = 0; x < kcp.ackcount; x++) {
					acklist[x * 2 + 0] = kcp.acklist[x * 2 + 0];
					acklist[x * 2 + 1] = kcp.acklist[x * 2 + 1];
				}
			}
			kcp.acklist = acklist;
			kcp.ackblock = newblock;
		}

		// TODO 该处有疑问 这里写个没调用的变量的意义是什么？
		// ptr := kcp.acklist[kcp.ackcount * 2:]
		// ptr[0] = sn
		// ptr[1] = ts
		kcp.ackcount++;
	}
	
	
	public static long ikcp_ack_getSn(Ikcpcb kcp,int p)
	{
		return kcp.acklist[p * 2 + 0];
		
	}
	
	public static long ikcp_ack_getTs(Ikcpcb kcp,int p)
	{
		return kcp.acklist[p * 2 + 1];
	}

	// //---------------------------------------------------------------------
	// // parse data
	// //---------------------------------------------------------------------
	/**
	 * 对收到的消息进行排序  然后放入排序好的消息队列
	 * @param kcp
	 * @param newseg
	 */
	public static void ikcp_parse_data(Ikcpcb kcp,IKCPSEG newseg)
	{
		long sn = newseg.sn;
		int repeat = 0;
		if (_itimediff(sn, kcp.rcv_nxt + kcp.rcv_wnd) >= 0
				|| _itimediff(sn, kcp.rcv_nxt) < 0) {
			return;
		}
		ListIterator<IKCPSEG> it = kcp.rcv_buf.listIterator();
		boolean isInsert = false;
		while (it.hasPrevious()) {
			IKCPSEG seg = it.previous();
			if (seg.sn == sn) {
				repeat = 1;
				isInsert = true;
				break;
			}
			if (_itimediff(sn, seg.sn) > 0) {
				it.add(newseg);
				isInsert = true;
				break;
			}
		}
		if (!isInsert)
			kcp.rcv_buf.addFirst(newseg);
		if (repeat == 0) {
			kcp.nrcv_buf++;
		}
		it = kcp.rcv_buf.listIterator();
		while (it.hasNext()) {
			IKCPSEG seg = it.next();
			if (seg.sn == kcp.rcv_nxt && kcp.nrcv_que < kcp.rcv_wnd) {
				it.remove();
				kcp.nrcv_buf--;
				kcp.rcv_queue.addLast(seg);
				// if kcp.user[0] == 0 {
				// fmt.Println("insert from recvqueue2", kcp.rcv_queue.Len(),
				// kcp.user)
				// }
				kcp.nrcv_que++;
				kcp.rcv_nxt++;
			} else {
				break;
			}
		}
		// //println("inputok!!!", kcp.nrcv_buf, kcp.nrcv_que)
	}
	
	/**
	 * 网络收到数据 放入kcp
	 * 收到一个下层数据包（比如UDP包）时需要调用：
	 * @param kcp
	 * @param data
	 * @param size  可读取的消息长度
	 * @return
	 */
	public static int Ikcp_input(Ikcpcb kcp,ByteBuf data,int size)
	{

		// if (ikcp_canlog(kcp, Ikcpcb.IKCP_LOG_INPUT)!=0) {
		// Ikcp_log(kcp, Ikcpcb.IKCP_LOG_INPUT, "[RI] %d bytes", size)
		// }

		if (data == null || size < 24) {
			return 0;
		}

		for (;;) {
			long ts, sn, _len, una, conv;
			int wnd;
			int cmd, frg;
			IKCPSEG seg;

			if (size < IKCP_OVERHEAD) {
				break;
			}

			conv = ikcp_decode32u(data);
			if (conv != kcp.conv) {
				return -1;
			}

			cmd = ikcp_decode8u(data);
			frg = ikcp_decode8u(data);
			wnd = ikcp_decode16u(data);
			ts = ikcp_decode32u(data);
			sn = ikcp_decode32u(data);
			una = ikcp_decode32u(data);
			_len = ikcp_decode32u(data);

			size -= IKCP_OVERHEAD;

			if (size < _len) {
				return -2;
			}

			if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK
					&& cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS) {
				return -3;
			}

			kcp.rmt_wnd = wnd;
			ikcp_parse_una(kcp, una);
			ikcp_shrink_buf(kcp);

			if (cmd == IKCP_CMD_ACK) {
				if (_itimediff(kcp.current, ts) >= 0) {
					Ikcp_update_ack(kcp, _itimediff(kcp.current, ts));
				}
				ikcp_parse_ack(kcp, sn);
				ikcp_shrink_buf(kcp);
				// if (ikcp_canlog(kcp, IKCP_LOG_IN_ACK)!=0) {
				// Ikcp_log(kcp, IKCP_LOG_IN_DATA,
				// "input ack: sn=%lu rtt=%ld rto=%ld", sn,
				// uint32(_itimediff(kcp.current, ts)),
				// uint32(kcp.rx_rto))
				// }
			} else if (cmd == IKCP_CMD_PUSH) {
				// if (ikcp_canlog(kcp, IKCP_LOG_IN_DATA)!=0) {
				// Ikcp_log(kcp, IKCP_LOG_IN_DATA,
				// "input psh: sn=%lu ts=%lu", sn, ts)
				// }
				if (_itimediff(sn, kcp.rcv_nxt + kcp.rcv_wnd) < 0) {
					ikcp_ack_push(kcp, sn, ts);
					if (_itimediff(sn, kcp.rcv_nxt) >= 0) {
						seg = ikcp_segment_new((int) _len);
						seg.conv = conv;
						seg.cmd = cmd;
						seg.frg = frg;
						seg.wnd = wnd;
						seg.ts = ts;
						seg.sn = sn;
						seg.una = una;
						seg._len = _len;

						if (_len > 0) {
							data.readBytes(seg.data, (int) _len);
						}

						ikcp_parse_data(kcp, seg);
					}
				}
			} else if (cmd == IKCP_CMD_WASK) {
				// ready to send back IKCP_CMD_WINS in Ikcp_flush
				// tell remote my window size
				kcp.probe |= IKCP_ASK_TELL;
				// if (ikcp_canlog(kcp, IKCP_LOG_IN_PROBE)!=0) {
				// Ikcp_log(kcp, IKCP_LOG_IN_PROBE, "input probe")
				// }
			} else if (cmd == IKCP_CMD_WINS) {
				// do nothing
				// if (ikcp_canlog(kcp, IKCP_LOG_IN_WIN)!=0) {
				// Ikcp_log(kcp, IKCP_LOG_IN_WIN,
				// "input wins: %lu", uint32(wnd))
				// }
			} else {
				return -3;
			}
			// data = data[_len:]
			size -= _len;
		}
		long una = kcp.snd_una;

		if (_itimediff(kcp.snd_una, una) > 0) {
			if (kcp.cwnd < kcp.rmt_wnd) {
				long mss = kcp.mss;
				if (kcp.cwnd < kcp.ssthresh) {
					kcp.cwnd++;
					kcp.incr += mss;
				} else {
					if (kcp.incr < mss) {
						kcp.incr = mss;
					}
					kcp.incr += (mss * mss) / kcp.incr + (mss / 16);
					if ((kcp.cwnd + 1) * mss >= kcp.incr) {
						kcp.cwnd++;
					}
				}
				if (kcp.cwnd > kcp.rmt_wnd) {
					kcp.cwnd = kcp.rmt_wnd;
					kcp.incr = kcp.rmt_wnd * mss;
				}
			}
		}
		return 0;
	}
	

	
	
	
	
	//---------------------------------------------------------------------
	// ikcp_encode_seg
	//---------------------------------------------------------------------
	public static void ikcp_encode_seg(ByteBuf ptr,IKCPSEG seg)
	{
		ikcp_encode32u(ptr, seg.conv);
		ikcp_encode8u(ptr, (int) seg.cmd);
		ikcp_encode8u(ptr, (int) (seg.frg));
		ikcp_encode16u(ptr, (int) (seg.wnd));
		ikcp_encode32u(ptr, seg.ts);
		ikcp_encode32u(ptr, seg.sn);
		ikcp_encode32u(ptr, seg.una);
		ikcp_encode32u(ptr, seg._len);
	}

	
	public static int ikcp_wnd_unused(Ikcpcb kcp)
	{
		 if (kcp.nrcv_que < kcp.rcv_wnd) {
		 return (int)(kcp.rcv_wnd - kcp.nrcv_que);
		 }
		 return 0;
	}
	
	
	 //---------------------------------------------------------------------
	 // Ikcp_flush
	 //---------------------------------------------------------------------
	public static void Ikcp_flush(Ikcpcb kcp)
	{
		long current = kcp.current;
		ByteBuf buffer = kcp.buffer;
		ByteBuf ptr = buffer;
		int count, size, i;
		long resent, cwnd;
		long rtomin;
		int change = 0;
		int lost = 0;
		IKCPSEG seg = new IKCPSEG();

		// 'Ikcp_update' haven't been called.
		if (kcp.updated == 0) {
			return;
		}

		seg.conv = kcp.conv;
		seg.cmd = IKCP_CMD_ACK;
		seg.frg = 0;
		seg.wnd = ikcp_wnd_unused(kcp);
		seg.una = kcp.rcv_nxt;
		seg._len = 0;
		seg.sn = 0;
		seg.ts = 0;

		// flush acknowledges
		size = 0;
		count = (int) kcp.ackcount;
		for (i = 0; i < count; i++) {
			// size = int32(ptr - buffer)
			if (size > kcp.mtu) {
				ikcp_output(kcp, buffer, size);
				ptr = buffer;
				size = 0;
			}
			seg.sn = ikcp_ack_getSn(kcp, i);
			seg.ts = ikcp_ack_getTs(kcp, i);
			ikcp_encode_seg(ptr, seg);
			size += 24;
		}

		kcp.ackcount = 0;

		// probe window size (if remote window size equals zero)
		if (kcp.rmt_wnd == 0) {
			if (kcp.probe_wait == 0) {
				kcp.probe_wait = IKCP_PROBE_INIT;
				kcp.ts_probe = kcp.current + kcp.probe_wait;
			} else {
				if (_itimediff(kcp.current, kcp.ts_probe) >= 0) {
					if (kcp.probe_wait < IKCP_PROBE_INIT) {
						kcp.probe_wait = IKCP_PROBE_INIT;
					}
					kcp.probe_wait += kcp.probe_wait / 2;
					if (kcp.probe_wait > IKCP_PROBE_LIMIT) {
						kcp.probe_wait = IKCP_PROBE_LIMIT;
					}
					kcp.ts_probe = kcp.current + kcp.probe_wait;
					kcp.probe |= IKCP_ASK_SEND;
				}
			}
		} else {
			kcp.ts_probe = 0;
			kcp.probe_wait = 0;
		}

		// flush window probing commands
		if ((kcp.probe & IKCP_ASK_SEND) != 0) {
			seg.cmd = IKCP_CMD_WASK;
			if (size > kcp.mtu) {
				ikcp_output(kcp, buffer, size);
				ptr = buffer;
				size = 0;
			}
			ikcp_encode_seg(ptr, seg);
			size += 24;
		}

		// flush window probing commands
		if ((kcp.probe & IKCP_ASK_TELL) != 0) {
			seg.cmd = IKCP_CMD_WINS;
			if (size > kcp.mtu) {
				ikcp_output(kcp, buffer, size);
				ptr = buffer;
				size = 0;
			}
			ikcp_encode_seg(ptr, seg);
			size += 24;
		}

		kcp.probe = 0;

		// calculate window size
		cwnd = _imin_(kcp.snd_wnd, kcp.rmt_wnd);
		if (kcp.nocwnd == 0) {
			cwnd = _imin_(kcp.cwnd, cwnd);
		}

		// move data from snd_queue to snd_buf
		// //println("check",kcp.snd_queue.Len())
//		int t = 0;
		ListIterator<IKCPSEG> p = kcp.snd_queue.listIterator();
		while (p.hasNext()) {
//			t++;
			if (_itimediff(kcp.snd_nxt, kcp.snd_una + cwnd) >= 0) {
				// if kcp.user[0] == 0 {
				// //fmt.Println("=======", kcp.snd_nxt, kcp.snd_una, cwnd)
				// }
				break;
			}
			IKCPSEG newseg = p.next();
			p.remove();
			kcp.snd_buf.addLast(newseg);
			// if kcp.user[0] == 0 {
			// println("debug check2:", t, kcp.snd_queue.Len(),
			// kcp.snd_buf.Len(),
			// kcp.nsnd_que)
			// }
			kcp.nsnd_que--;
			kcp.nsnd_buf++;

			newseg.conv = kcp.conv;
			newseg.cmd = IKCP_CMD_PUSH;
			newseg.wnd = seg.wnd;
			newseg.ts = current;
			newseg.sn = kcp.snd_nxt;
			kcp.snd_nxt++;
			newseg.una = kcp.rcv_nxt;
			newseg.resendts = current;
			newseg.rto = kcp.rx_rto;
			newseg.fastack = 0;
			newseg.xmit = 0;

		}

		// calculate resent
		resent = kcp.fastresend;
		if (kcp.fastresend <= 0) {
			resent = 0xffffffff;
		}
		rtomin = (kcp.rx_rto >> 3);
		if (kcp.nodelay != 0) {
			rtomin = 0;
		}

//		int a = 0;
		// flush data segments
		p = kcp.snd_buf.listIterator();
		while (p.hasNext()) {
			// //println("debug loop", a, kcp.snd_buf.Len())
//			a++;
			IKCPSEG segment = p.next();
			int needsend = 0;
			if (segment.xmit == 0) {
				needsend = 1;
				segment.xmit++;
				segment.rto = kcp.rx_rto;
				segment.resendts = current + segment.rto + rtomin;
			} else if (_itimediff(current, segment.resendts) >= 0) {
				needsend = 1;
				segment.xmit++;
				kcp.xmit++;
				if (kcp.nodelay == 0) {
					segment.rto += kcp.rx_rto;
				} else {
					segment.rto += kcp.rx_rto / 2;
				}
				segment.resendts = current + segment.rto;
				lost = 1;
			} else if (segment.fastack >= resent) {
				needsend = 1;
				segment.xmit++;
				segment.fastack = 0;
				segment.resendts = current + segment.rto;
				change++;
			}
			if (needsend != 0) {
				int need;
				segment.ts = current;
				segment.wnd = seg.wnd;
				segment.una = kcp.rcv_nxt;

				need = (int) (IKCP_OVERHEAD + segment._len);

				// //fmt.Printf("vzex:need send%d, %d,%d,%d\n", kcp.nsnd_buf,
				// size, need,
				// kcp.mtu)
				if (size + need >= kcp.mtu) {
					// //fmt.Printf("trigger!\n");
					ikcp_output(kcp, buffer, size);
					ptr = buffer;
					size = 0;
				}

				ikcp_encode_seg(ptr, segment);
				size += 24;

				if (segment._len > 0) {

					ptr.readBytes(ptr, (int) segment._len);
					size += segment._len;
				}

				if (segment.xmit >= kcp.dead_link) {
					kcp.state = 0;
				}
			}
		}

		// flash remain segments
		if (size > 0) {
			ikcp_output(kcp, buffer, size);
		}

		// update ssthresh
		if (change != 0) {
			long inflight = kcp.snd_nxt - kcp.snd_una;
			kcp.ssthresh = inflight / 2;
			if (kcp.ssthresh < IKCP_THRESH_MIN) {
				kcp.ssthresh = IKCP_THRESH_MIN;
			}
			kcp.cwnd = kcp.ssthresh + resent;
			kcp.incr = kcp.cwnd * kcp.mss;
		}

		if (lost != 0) {
			kcp.ssthresh = cwnd / 2;
			if (kcp.ssthresh < IKCP_THRESH_MIN) {
				kcp.ssthresh = IKCP_THRESH_MIN;
			}
			kcp.cwnd = 1;
			kcp.incr = kcp.mss;
		}

		if (kcp.cwnd < 1) {
			kcp.cwnd = 1;
			kcp.incr = kcp.mss;
		}
	}
	
	
	/**
	 *  以一定频率调用 ikcp_update来更新 kcp状态，并且传入当前时钟（毫秒单位）
		如 10ms调用一次，或用 ikcp_check确定下次调用 update的时间不必每次调用
	 * @param kcp
	 * @param current
	 */
	public static void Ikcp_update(Ikcpcb kcp,long current)
	{
		int slap;

		kcp.current = current;

		if (kcp.updated == 0) {
			kcp.updated = 1;
			kcp.ts_flush = kcp.current;
		}

		slap = _itimediff(kcp.current, kcp.ts_flush);

		if (slap >= 10000 || slap < -10000) {
			kcp.ts_flush = kcp.current;
			slap = 0;
		}

		if (slap >= 0) {
			kcp.ts_flush += kcp.interval;
			if (_itimediff(kcp.current, kcp.ts_flush) >= 0) {
				kcp.ts_flush = kcp.current + kcp.interval;
			}
			Ikcp_flush(kcp);
		}
	}
	
	/**
	 * 用于确定下次调用Ikcp_update的时间
	 * @param kcp
	 * @param current
	 * @return
	 */
	public static long Ikcp_check(Ikcpcb kcp,long current)
	{
		long ts_flush = kcp.ts_flush;
		long tm_flush = 0x7fffffff;
		long tm_packet = 0x7fffffff;
		int minimal = 0;
		if (kcp.updated == 0) {
			return current;
		}

		if (_itimediff(current, ts_flush) >= 10000
				|| _itimediff(current, ts_flush) < -10000) {
			ts_flush = current;
		}

		if (_itimediff(current, ts_flush) >= 0) {
			return current;
		}

		tm_flush = _itimediff(ts_flush, current);

		Iterator<IKCPSEG> p = kcp.snd_buf.listIterator();
		while (p.hasNext()) {

			IKCPSEG seg = p.next();
			long diff = _itimediff(seg.resendts, current);
			if (diff <= 0) {
				return current;
			}
			if (diff < tm_packet) {
				tm_packet = diff;
			}
		}

		minimal = (int) tm_packet;
		if (tm_packet >= tm_flush) {
			minimal = (int) tm_flush;
		}
		if (minimal >= kcp.interval) {
			minimal = (int) kcp.interval;
		}

		return current + minimal;
	}
	
	public static int Ikcp_setmtu(Ikcpcb kcp ,int mtu)
	{
		 if (mtu < 50 || mtu < IKCP_OVERHEAD) {
		 return -1;
		 }
		 ByteBuf buffer = PooledByteBufAllocator.DEFAULT.directBuffer((mtu + IKCP_OVERHEAD) * 3);
		 buffer.order(ByteOrder.LITTLE_ENDIAN);
		 kcp.mtu = mtu;
		 kcp.mss = kcp.mtu - IKCP_OVERHEAD;
		 kcp.buffer = buffer;
		 return 0;
	}
	
	public static int ikcp_interval(Ikcpcb kcp,int interval)
	{
		if (interval > 5000) {
			interval = 5000;
		} else if (interval < 10) {
			interval = 10;
		}
		kcp.interval = interval;
		return 0;
	}
	/**
	 * 普通模式：`ikcp_nodelay(kcp, 0, 40, 0, 0); 极速模式： ikcp_nodelay(kcp, 1, 10, 2, 1);
	 * @param kcp
	 * @param nodelay 是否启用 nodelay模式，0不启用；1启用
	 * @param interval 协议内部工作的 interval，单位毫秒，比如 10ms或者 20ms
	 * @param resend 快速重传模式，默认0关闭，可以设置2（2次ACK跨越将会直接重传）
	 * @param nc  nc ：是否关闭流控，默认是0代表不关闭，1代表关闭。
	 * @return
	 */
	public static int Ikcp_nodelay(Ikcpcb kcp ,int nodelay,int interval,int resend,int nc)
	{
		if (nodelay >= 0) {
			kcp.nodelay = nodelay;
			if (nodelay != 0) {
				kcp.rx_minrto = IKCP_RTO_NDL;
			} else {
				kcp.rx_minrto = IKCP_RTO_MIN;
			}
		}
		if (interval >= 0) {
			if (interval > 5000) {
				interval = 5000;
			} else if (interval < 10) {
				interval = 10;
			}
			kcp.interval = interval;
		}
		if (resend >= 0) {
			kcp.fastresend = resend;
		}
		if (nc >= 0) {
			kcp.nocwnd = nc;
		}
		return 0;
	}
	
	public static int Ikcp_wndsize(Ikcpcb kcp,int sndwnd,int rcvwnd )
	{

		if (sndwnd > 0) {
			kcp.snd_wnd = sndwnd;
		}
		if (rcvwnd > 0) {
			kcp.rcv_wnd = rcvwnd;
		}
		return 0;
	}
	
	public static int Ikcp_waitsnd(Ikcpcb kcp)
	{
		return (int)(kcp.nsnd_buf + kcp.nsnd_que);
	}
}
